import random
from typing import Optional
import time
import traceback
from playwright.sync_api import TimeoutError as PlaywrightTimeoutError








def humanized_click(
        page,
        xpath: str,
        wait_timeout: float = 10_000,  # 元素可见等待超时（毫秒），默认10秒
        pre_click_delay: Optional[float] = None,  # 点击前随机延迟（秒），默认0.5-1秒随机
        click_timeout: float = 10_000,  # 点击操作超时（毫秒），默认10秒
        post_click_delay: float = 1.0,  # 点击后固定延迟（秒）
        max_retries: int = 2,  # 最大重试次数
        retry_delay: float = 1.0  # 重试间隔（秒）
) -> bool:
    """
    拟人化点击封装：带随机延迟、双重超时控制、重试机制
    """
    for attempt in range(max_retries + 1):
        try:
            # 1. 等待元素可见（Playwright 内置智能等待）
            locator = page.locator(f'xpath={xpath}')
            
            # 检查元素是否存在
            count = locator.count()
            if count == 0:
                print(f"⚠️ 元素不存在: {xpath}")
                if attempt < max_retries:
                    print(f"尝试重试 ({attempt+1}/{max_retries})...")
                    time.sleep(retry_delay)
                    continue
                return False
            
            # 等待元素可见
            locator.wait_for(
                state="visible",
                timeout=wait_timeout
            )
            
            # 检查元素是否可点击（不被其他元素遮挡）
            is_visible = locator.is_visible()
            is_enabled = locator.is_enabled()
            if not is_visible or not is_enabled:
                print(f"⚠️ 元素不可交互: 可见={is_visible}, 启用={is_enabled}")
                if attempt < max_retries:
                    print(f"尝试重试 ({attempt+1}/{max_retries})...")
                    time.sleep(retry_delay)
                    continue
                return False

            # 2. 点击前随机延迟（模拟人类操作）
            delay = pre_click_delay or random.uniform(0.5, 1.0)
            time.sleep(delay)

            # 3. 执行点击（带独立超时控制）
            locator.click(timeout=click_timeout)

            # 4. 点击后固定延迟（等待页面响应）
            time.sleep(post_click_delay)
            return True

        except PlaywrightTimeoutError as e:
            print(f"⚠️ 点击超时: {xpath}")
            if attempt < max_retries:
                print(f"尝试重试 ({attempt+1}/{max_retries})...")
                time.sleep(retry_delay)
            else:
                print(f"重试次数已用完，操作失败: {str(e)}")
                return False
        except Exception as e:
            print(f"⚠️ 操作失败: {str(e)}")
            print(traceback.format_exc())
            if attempt < max_retries:
                print(f"尝试重试 ({attempt+1}/{max_retries})...")
                time.sleep(retry_delay)
            else:
                return False
    
    return False


def humanized_click2(
        page,
        xpath: str,
        wait_timeout: float = 10_000,  # 元素可见等待超时（毫秒），默认10秒
        pre_click_delay: Optional[float] = 0.1,  # 点击前随机延迟（秒），默认0.5-1秒随机
        click_timeout: float = 10_000,  # 点击操作超时（毫秒），默认10秒
        post_click_delay: float = 0.1,  # 点击后固定延迟（秒）
        max_retries: int = 2,  # 最大重试次数
        retry_delay: float = 1.0  # 重试间隔（秒）
) -> bool:
    """
    拟人化点击封装：带随机延迟、双重超时控制、重试机制
    """
    for attempt in range(max_retries + 1):
        try:
            # 1. 等待元素可见（Playwright 内置智能等待）
            locator = page.locator(f'xpath={xpath}')

            # 检查元素是否存在
            count = locator.count()
            if count == 0:
                print(f"⚠️ 元素不存在: {xpath}")
                if attempt < max_retries:
                    print(f"尝试重试 ({attempt + 1}/{max_retries})...")
                    time.sleep(retry_delay)
                    continue
                return False

            # 等待元素可见
            locator.wait_for(
                state="visible",
                timeout=wait_timeout
            )

            # 检查元素是否可点击（不被其他元素遮挡）
            is_visible = locator.is_visible()
            is_enabled = locator.is_enabled()
            if not is_visible or not is_enabled:
                print(f"⚠️ 元素不可交互: 可见={is_visible}, 启用={is_enabled}")
                if attempt < max_retries:
                    print(f"尝试重试 ({attempt + 1}/{max_retries})...")
                    time.sleep(retry_delay)
                    continue
                return False

            # 2. 点击前随机延迟（模拟人类操作）
            delay = pre_click_delay or random.uniform(0.5, 1.0)
            time.sleep(delay)

            # 3. 执行点击（带独立超时控制）
            locator.click(timeout=click_timeout)

            # 4. 点击后固定延迟（等待页面响应）
            time.sleep(post_click_delay)
            return True

        except PlaywrightTimeoutError as e:
            print(f"⚠️ 点击超时: {xpath}")
            if attempt < max_retries:
                print(f"尝试重试 ({attempt + 1}/{max_retries})...")
                time.sleep(retry_delay)
            else:
                print(f"重试次数已用完，操作失败: {str(e)}")
                return False
        except Exception as e:
            print(f"⚠️ 操作失败: {str(e)}")
            print(traceback.format_exc())
            if attempt < max_retries:
                print(f"尝试重试 ({attempt + 1}/{max_retries})...")
                time.sleep(retry_delay)
            else:
                return False

    return False


def safe_operation(func, *args, max_retries=2, retry_delay=1.0, **kwargs):
    """
    安全执行任意操作的通用函数，带重试机制
    
    参数:
        func: 要执行的函数
        *args: 传递给函数的位置参数
        max_retries: 最大重试次数
        retry_delay: 重试间隔（秒）
        **kwargs: 传递给函数的关键字参数
    
    返回:
        函数执行结果或None（如果全部失败）
    """
    for attempt in range(max_retries + 1):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            print(f"⚠️ 操作失败: {str(e)}")
            print(traceback.format_exc())
            if attempt < max_retries:
                print(f"尝试重试 ({attempt+1}/{max_retries})...")
                time.sleep(retry_delay)
    
    return None

